home *** CD-ROM | disk | FTP | other *** search
/ Power Hacker 2003 / Power_Hacker_2003.iso / Exploit and vulnerability / w00w00 / articles / lkmhack.txt < prev   
Encoding:
Text File  |  1998-08-13  |  8.9 KB  |  296 lines

  1. w00w00!
  2.  
  3. lkm: Kernel hacking made easy
  4. By: w00w00 Security Development article by Nicolas Dubee
  5.  
  6. The following applies to the Linux i86 2.0.x kernel series.
  7. It may also be accurate for previous releases, but has not been
  8. tested. 2.1.x kernels introduced a bunch of changes, notably in
  9. the memory managment routines, and are not discussed here.
  10.  
  11. Thanks to Halflife who first got the idea to use lkm for malicious
  12. purposes, and tiepilot, my living hero.
  13.  
  14.  
  15.  
  16. User space vs. Kernel space
  17. ---------------------------
  18.  
  19. Linux is a protected operating system. It is implemented over the
  20. protected mode of the i386 series of CPUs.
  21.  
  22. Memory is divided into roughly two parts: kernel space and user space.
  23. Kernel space is where the kernel code lives, and user space is where 
  24. the user programs live. Of course, a given user program can't write to
  25. kernel memory or to another program's memory area.
  26.  
  27. Unfortunately, this is also the case for kernel code. Kernel code
  28. can't write to user space either. What does this mean? Well, when a given 
  29. hardware driver wants to write data bytes to a program in user memory, it
  30. can't do it directly, but rather it must use specific kernel functions
  31. instead. Also, when paramaters are passed by address to a kernel function,
  32. the kernel function can not read the parameters directly. It must use
  33. other kernel functions to read each byte of the parameters.
  34.  
  35. Here are a few useful functions to use in kernel mode for transferring
  36. data bytes to or from user memory.
  37.  
  38. #include <asm/segment.h>
  39.  
  40. get_user(ptr)
  41.  Gets the given byte, word, or long from user memory. This is a macro,
  42.  and it relies on the type of the argument to determine the number of bytes
  43.  to transfer. You then have to use typecasts wisely.
  44.  
  45. put_user(ptr)
  46.  This is the same as get_user(), but instead of reading, it writes data
  47.  bytes to user memory.
  48.  
  49. memcpy_fromfs(void *to, const void *from,unsigned long n)
  50.  Copies n bytes from *from in user memory to *to in kernel memory.
  51.  
  52. memcpy_tofs(void *to,const *from,unsigned long n)
  53.  Copies n bytes from *from in kernel memory to *to in user memory.
  54.  
  55.  
  56.  
  57. System calls
  58. ------------
  59.  
  60. Most libc calls rely on system calls, which are the simplest kernel
  61. functions a user program can call. These system calls are implemented
  62. in the kernel itself or in loadable kernel modules, which are little
  63. chunks of dynamically linkable kernel code.
  64.  
  65. Like MS-DOS and many others, Linux system calls are implemented through
  66. a multiplexor called with a given maskable interrupt. In Linux,
  67. this interrupt is int 0x80. When the 'int 0x80' instruction is executed,
  68. control is given to the kernel (or, more accurately, to the function 
  69. _system_call()), and the actual demultiplexing process occurs.
  70.  
  71. * How does _system_call() work ?
  72.  
  73. First, all registers are saved and the content of the %eax register
  74. is checked against the global system calls table, which enumerates
  75. all system calls and their addresses.
  76. This table can be accessed with the extern void *sys_call_table[] variable. 
  77. A given number and memory address in this table corresponds to each system
  78. call. System call numbers can be found in /usr/include/sys/syscall.h. 
  79. They are of the form SYS_systemcallname. If the system call is not
  80. implemented, the corresponding cell in the sys_call_table is 0, and an
  81. error is returned. Otherwise, the system call exists and the corresponding
  82. entry in the table is the memory address of the system call code. 
  83.  
  84. Here is an example of an invalid system call:
  85.  
  86. [root@plaguez kernel]# cat no1.c
  87. #include <linux/errno.h>
  88. #include <sys/syscall.h>
  89. #include <errno.h>
  90.  
  91. extern void *sys_call_table[];
  92.  
  93. sc()
  94. { // system call number 165 doesn't exist at this time.
  95.     __asm__(
  96.         "movl $165,%eax
  97.              int $0x80");
  98. }
  99.  
  100. main()
  101. {
  102.     errno = -sc();
  103.     perror("test of invalid syscall");
  104. }
  105. [root@plaguez kernel]# gcc no1.c
  106. [root@plaguez kernel]# ./a.out
  107. test of invalid syscall: Function not implemented
  108. [root@plaguez kernel]# exit
  109.  
  110.  
  111. The control is then transferred to the actual system call, which performs
  112. whatever you requested and returns. _system_call() then calls
  113. _ret_from_sys_call() to check various stuff, and ultimately returns to user
  114. memory.
  115.  
  116.  
  117. * libc
  118.  
  119. The int $0x80 isn't used directly for system calls; rather, libc
  120. functions, which are often wrappers to interrupt 0x80, are used.
  121.  
  122. libc generally features the system calls using the _syscallX() macros, where
  123. X is the number of parameters for the system call.
  124.  
  125. For example, the libc entry for write(2) would be implemented with a _syscall3
  126. macro, since the actual write(2) prototype requires 3 parameters.
  127. Before calling interrupt 0x80, the _syscallX macros are supposed to
  128. set up the stack frame and the argument list required for the system call.
  129. Finally, when the _system_call() (which is triggered with int $0x80) returns,
  130. the _syscallX() macro will check for a negative return value (in %eax)
  131. and will set errno accordingly.
  132.  
  133. Let's check another example with write(2) and see how it gets preprocessed. 
  134.  
  135. [root@plaguez kernel]# cat no2.c
  136. #include <linux/types.h>
  137. #include <linux/fs.h>
  138. #include <sys/syscall.h>
  139. #include <asm/unistd.h>
  140. #include <sys/types.h>
  141. #include <stdio.h>
  142. #include <errno.h>
  143. #include <fcntl.h>
  144. #include <ctype.h>
  145.  
  146. _syscall3(ssize_t,write,int,fd,const void *,buf,size_t,count);
  147.  
  148. main()
  149. {
  150.     char *t = "this is a test.\n";
  151.     write(0, t, strlen(t));
  152. }
  153. [root@plaguez kernel]# gcc -E no2.c > no2.C
  154. [root@plaguez kernel]# indent no2.C -kr
  155. indent:no2.C:3304: Warning: old style assignment ambiguity in "=-".  Assuming "= -"
  156.  
  157. [root@plaguez kernel]# tail -n 50 no2.C
  158.  
  159.  
  160. #9 "no2.c" 2
  161.  
  162.  
  163.  
  164.  
  165. ssize_t write(int fd, const void *buf, size_t count)
  166. {
  167.     long __res;
  168.     __asm__ __volatile("int $0x80":"=a"(__res):"0"(4), "b"((long) (fd)), "c"((long) (buf)), "d"((long) (count)));
  169.     if (__res >= 0)
  170.     return (ssize_t) __res;
  171.     errno = -__res;
  172.     return -1;
  173. };
  174.  
  175. main()
  176. {
  177.     char *t = "this is a test.\n";
  178.     write(0, t, strlen(t));
  179. }
  180. [root@plaguez kernel]# exit
  181.  
  182.  
  183. Note that the "0"(4) in the write() function above matches the SYS_write
  184. definition in /usr/include/sys/syscall.h. 
  185.  
  186.  
  187.  
  188.  
  189. * Making your own system calls.
  190.  
  191. There are a few ways to make your own system calls.
  192. For example, you could modify the kernel sources and append your own code.
  193. A far easier way, however, would be to write a loadable kernel module.
  194.  
  195. A loadable kernel module is nothing more than an object file containing
  196. code that will be dynamically linked into the kernel when it is needed.
  197.  
  198. The main purposes of this feature are to have a small kernel, and to load
  199. a given driver when it is needed with the insmod(1) command.
  200. It's also easier to write a lkm than to write code in the kernel source tree.
  201.  
  202. * Writing a lkm
  203.  
  204. A lkm is easily made in C.
  205. It contains a chunk of #defines, some functions, an initialization function
  206. called init_module(), and an unload function called cleanup_module().
  207.  
  208. Here is a typical lkm source structure:
  209.  
  210.  
  211. #define MODULE
  212. #define __KERNEL__
  213. #define __KERNE_SYSCALLS__
  214.  
  215. #include <linux/config.h>
  216. #ifdef MODULE
  217. #include <linux/module.h>
  218. #include <linux/version.h>
  219. #else
  220. #define MOD_INC_USE_COUNT
  221. #define MOD_DEC_USE_COUNT
  222. #endif
  223.  
  224. #include <linux/types.h>
  225. #include <linux/fs.h>
  226. #include <linux/mm.h>
  227. #include <linux/errno.h>
  228. #include <asm/segment.h>
  229. #include <sys/syscall.h>
  230. #include <linux/dirent.h>
  231. #include <asm/unistd.h>
  232. #include <sys/types.h>
  233. #include <stdio.h>
  234. #include <errno.h>
  235. #include <fcntl.h>
  236. #include <ctype.h>
  237.  
  238. int errno;
  239.  
  240. char tmp[64];
  241.  
  242. /* for example, we may need to use ioctl */
  243. _syscall3(int, ioctl, int, d, int, request, unsigned long, arg);
  244.  
  245. int myfunction(int parm1,char *parm2)
  246. {
  247.    int i,j,k;
  248.    /* ... */
  249. }
  250.  
  251. int init_module(void)
  252. {
  253.    /* ... */
  254.    printk("\nModule loaded.\n");
  255.    return 0;
  256. }
  257.  
  258. void cleanup_module(void)
  259. {
  260.    /* ... */
  261. }
  262.  
  263. Check the mandatory #defines (#define MODULE, #define __KERNEL__) and
  264. #includes (#include <linux/config.h> ...)
  265.  
  266. Also note that as our lkm will be running in kernel mode, we can't use
  267. libc functions, but we can use system calls with the previously
  268. discussed _syscallX() macros.
  269.  
  270. You would compile this module with 'gcc -c -O3 module.c' and insert it
  271. into the kernel with 'insmod module.o' (optimization must be turned
  272. on).
  273.  
  274. As the title suggests, lkm can also be used to modify kernel code without
  275. having to rebuild it entirely. For example, you could patch the write(2)
  276. system call to hide portions of a given file.
  277. Seems like a good place for backdoors, too: what would you do if you
  278. couldn't trust your own kernel?
  279.  
  280.  
  281.  
  282. * Kernel and system calls backdoors
  283.  
  284. The main idea behind this is pretty simple. We'll redirect those damn
  285. system calls to our own ones in a lkm, which will enable us to force the
  286. kernel to react as we want it to.
  287. For example, we could hide a sniffer by 
  288. patching the IOCTL system call and masking the PROMISC bit. Lame but
  289. efficient.
  290.  
  291. To modify a given system call, just add the definition of the
  292. extern void *sys_call_table[] in your lkm, and have the init_module()
  293. function modify the corresponding entry in the sys_call_table to point to
  294. your own code. The modified call can then do whatever you wish it to, call
  295. the original system call by modifying sys_call_table once more, and ...
  296.